//===- lli.cpp - LLVM Interpreter / Dynamic compiler ----------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This utility provides a simple wrapper around the LLVM Execution Engines,
// which allow the direct execution of LLVM programs through a Just-In-Time
// compiler, or through an interpreter if no JIT is available for this platform.
//
//===----------------------------------------------------------------------===//

#ifndef EMULATOR_IR_GENERATOR_H
#define EMULATOR_IR_GENERATOR_H

#include "instr.h"
#include "llvm/LLVMContext.h"
#include "llvm/Module.h"
#include "llvm/Type.h"
#include "llvm/InstrTypes.h"
#include "llvm/Support/Host.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Bitcode/ReaderWriter.h"
#include "llvm/Function.h"
#include "llvm/Support/IRBuilder.h"
#include "llvm/CodeGen/LinkAllCodegenComponents.h"
#include "llvm/ExecutionEngine/GenericValue.h"
#include "llvm/ExecutionEngine/JIT.h"
#include "llvm/ExecutionEngine/JITEventListener.h"
#include "llvm/ExecutionEngine/JITMemoryManager.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/PluginLoader.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/TargetSelect.h"
#include <cerrno>

using namespace llvm;

#define CRACK_INS(x)

class EmulatorIRGenerator 
{
private:
  /// mask for extracting register value.
  static const uint64_t size_mask[6];

private:
  /// private contor.
  EmulatorIRGenerator() { irBuilder = new IRBuilder<>(getGlobalContext());}

private:
  IRBuilder<> * irBuilder; 
  Function *current; // The current function operating on.

private:
  // ========================================
  // Functions to assist the generation of IR 
  // for instructions
  // ========================================
  void setInsertionPoint(BasicBlock *BB) { irBuilder->SetInsertPoint(BB); }
  void generateSideExit(BasicBlock *&FallBB, BasicBlock *&TargBB)
  {
    TargBB = BasicBlock::Create(getGlobalContext(), current->getName(), current);
    FallBB = BasicBlock::Create(getGlobalContext(), current->getName(), current);
  }
  Value* generateMaskedVal(Value* Val, uint64_t mask)
  {
    LLVMContext &Context = getGlobalContext();
    Value *ValMask = ConstantInt::get(Type::getInt64Ty(Context), mask);
    return irBuilder->CreateAnd(Val, ValMask);  
  }
  Value* generateShiftAndMask(Value *val1, Value* val2, uint64_t mask)
  {
    LLVMContext &Context = getGlobalContext();
    Value *masked = generateMaskedVal(val1, ~mask);
    val2 = irBuilder->CreateZExt(val2, Type::getInt64Ty(Context));
    return irBuilder->CreateOr(masked, val2);  
  }
  Value* generateGetField(uint32_t offset)
  {
    LLVMContext &Context = getGlobalContext();
    Value *BaseAddr = current->arg_begin();
    Value *Offset = ConstantInt::get(Type::getInt64Ty(Context), offset);
    return irBuilder->CreateAdd(BaseAddr, Offset);
  }

  Type* getInstruction_osType(bxInstruction_c *i)
  {
    if (i->os64L()) return Type::getInt64Ty(getGlobalContext());
    if (i->os32L()) return Type::getInt32Ty(getGlobalContext());
    return Type::getInt16Ty(getGlobalContext());
  }

  // ========================================
  // Functions to resolve linear memory address.
  // ========================================
  Value *generateGetWordLinAddress(bxInstruction_c *i)
  {
    Value *LinAddr = 0;
    if(i->sib()) 
    {
      // (Bit16u) (BX_READ_16BIT_REG(i->sibBase()) + BX_READ_16BIT_REG(i->sibIndex()) + i->displ16s());
      Value *SR = generateRead16GPR(i->sibBase());
      Value *IR = generateRead16GPR(i->sibIndex());
      Value *AIR = irBuilder->CreateAdd(SR, IR);
      Value *FLA = irBuilder->CreateAdd(AIR, ConstantInt::get(IR->getType(), i->displ16s()));
      LinAddr = irBuilder->CreateTrunc(FLA, Type::getInt16Ty(getGlobalContext())); // truncate.
    }
    return LinAddr;
  }

  Value *generateGetDwordLinAddress(bxInstruction_c *i)
  {
    Value *LinAddr = 0;
    if(i->sib()) 
    {
      // (Bit32u) (BX_READ_32BIT_REG(i->sibBase()) + (BX_READ_32BIT_REG(i->sibIndex()) << i->sibScale()) + i->displ32s());
      Value *SR =  generateRead32GPR(i->sibBase());
      Value *IR = generateRead32GPR(i->sibIndex());
      Value *SIR = irBuilder->CreateShl(IR, ConstantInt::get(IR->getType(), i->sibScale())) ;
      Value *AIR = irBuilder->CreateAdd(SR, SIR);
      Value *FLA = irBuilder->CreateAdd(AIR, ConstantInt::get(SIR->getType(), i->displ32s()));
      LinAddr = irBuilder->CreateTrunc(FLA, Type::getInt32Ty(getGlobalContext())); // truncate.
    }
    else 
    {
      // (Bit32u) (BX_READ_32BIT_REG(i->sibBase()) + i->displ32s());
      Value *SR  =  generateRead32GPR(i->sibBase());
      Value *FLA = irBuilder->CreateAdd(SR, ConstantInt::get(SR->getType(), i->displ32s()));
      LinAddr = irBuilder->CreateTrunc(FLA, Type::getInt32Ty(getGlobalContext())); // truncate.
    }
    return LinAddr;
  }

  Value *generateGetQwordLinAddress(bxInstruction_c *i)
  {
    Value *LinAddr = 0;
    if(i->sib()) 
    {
      // BX_READ_64BIT_REG(i->sibBase()) + (BX_READ_64BIT_REG(i->sibIndex()) << i->sibScale()) + i->displ32s();
      Value *SR =  generateRead64GPR(i->sibBase());
      Value *IR = generateRead64GPR(i->sibIndex());
      Value *SIR = irBuilder->CreateShl(IR, ConstantInt::get(IR->getType(), i->sibScale())) ;
      Value *AIR = irBuilder->CreateAdd(SR, SIR);
      LinAddr = irBuilder->CreateAdd(AIR, ConstantInt::get(SIR->getType(), i->displ32s()));
    }
    else 
    {
      // BX_READ_64BIT_REG(i->sibBase()) + i->displ32s();
      Value *SR =  generateRead64GPR(i->sibBase());
      LinAddr = irBuilder->CreateAdd(SR, ConstantInt::get(SR->getType(), i->displ32s()));
    }
    return LinAddr;
  }

  Value *generateInstruction_GetLinAddress(bxInstruction_c *i)
  {
    Value *LinAddr = 0;
    if (i->as64L()) LinAddr = generateGetQwordLinAddress(i);
    else if (i->as32L())  LinAddr = generateGetDwordLinAddress(i);
    else LinAddr = generateGetWordLinAddress(i);  
    return LinAddr;
  }

  // ========================================
  // Functions to read and update the value 
  // of special registers.
  // ========================================
  Value* generateReadEntireSGR(uint8_t index);
  void generateInstruction_ReadSGR(bxInstruction_c *i)
  {
#if 0
    // return (Bit32u) BX_CPU_THIS_PTR sregs[seg].cache.u.segment.base + offset;
    // 
    // if (seg < BX_SEG_REG_FS)
    // return offset;
    // else
    // return BX_CPU_THIS_PTR sregs[seg].cache.u.segment.base + offset;

    // Get the SGR value.
    Type  *RegTy = PointerType::getUnqual(Type::getInt64Ty(getGlobalContext()));
    Value *EffAddr = generateReadEntireSGR(i->seg());
    Value *LoadPtr = irBuilder->CreateIntToPtr(EffAddr, RegTy);
    // Get the SGR base value.
#endif
  }
  void generateUpdateRIP(bxInstruction_c *i)
  {
    Value *RIP = generateRead64GPR(17); 
    RIP = irBuilder->CreateAdd(RIP, ConstantInt::get(RIP->getType(), i->ilen())); 
    generateWrite64GPR(17, RIP);
  }
  void generateUpdatePrevRIP(bxInstruction_c *i);
  void generateUpdateInstCount(bxInstruction_c *i)
  {
    LLVMContext &Context = getGlobalContext();
    Type  *RegTy = PointerType::getUnqual(Type::getInt64Ty(Context));
    Value *EffAddr = generateGetField(40);
    Value *StorePtr = irBuilder->CreateIntToPtr(EffAddr, RegTy);
    Value *InstCount = irBuilder->CreateLoad(StorePtr); 
    InstCount = irBuilder->CreateAdd(InstCount, ConstantInt::get(InstCount->getType(), 1)); 
    irBuilder->CreateStore(InstCount, StorePtr); 
  }

  // ========================================
  // Functions to read the register value.
  // ========================================
  Value* generateReadEntireGPR(uint8_t index);
  Value* generateRead8GPR(uint8_t index, uint8_t low)
  {
    if (low) return generateMaskedVal(generateReadEntireGPR(index), size_mask[0]);
    //else return generateShiftAndMask(generateReadEntireGPR(index), size_mask[1]);
  }
  Value* generateRead16GPR(uint8_t index)
  {
    return generateMaskedVal(generateReadEntireGPR(index), size_mask[2]);
  }
  Value* generateRead32GPR(uint8_t index)
  {
    return generateMaskedVal(generateReadEntireGPR(index), size_mask[3]);
  }
  Value* generateRead64GPR(uint8_t index)
  {
    return generateMaskedVal(generateReadEntireGPR(index), size_mask[4]);
  }
  Value *generateReadGPR(uint8_t index, bxInstruction_c *i)
  {
    if (i->os64L()) return generateRead64GPR(index);
    if (i->os32L()) return generateRead32GPR(index);
    return generateRead16GPR(index);
  }

  // ========================================
  // Functions to write the register value.
  // To write to a register. the full value, i.e.
  // 64 bits value of the register is loaded.
  // the part to write is masked out and the
  // value to write it or'ed in. lastly, the
  // final value is written back to the 64 bit
  // register. Hopefully, optimizer can produce
  // optimal sequence for this.
  // ========================================
  Value* generateWriteEntireGPR(uint8_t index, Value *Val);
  void generateWrite8GPR(uint8_t  index, Value* val, uint8_t low)
  {
    if (low) generateWriteEntireGPR(index, generateShiftAndMask(generateRead64GPR(index), val, size_mask[0]));
    else generateWriteEntireGPR(index, generateShiftAndMask(generateRead64GPR(index), val, size_mask[1]));
  }
  void generateWrite16GPR(uint8_t index, Value* val)
  {
    generateWriteEntireGPR(index, generateShiftAndMask(generateRead64GPR(index), val, size_mask[2]));
  }
  void generateWrite32GPR(uint8_t index, Value* val)
  {
    generateWriteEntireGPR(index, generateShiftAndMask(generateRead64GPR(index), val, size_mask[3]));
  }
  void generateWrite64GPR(uint8_t index, Value* val)
  {
    generateWriteEntireGPR(index, generateShiftAndMask(generateRead64GPR(index), val, size_mask[4]));
  }
  void generateWriteGPR(uint8_t index, Value *val, bxInstruction_c *i)
  {
    // upper 32 bit are zero-extended for 32 bit write.
    if (i->os64L() || i->os32L()) generateWrite64GPR(index, val);
    else generateWrite16GPR(index, val);
  }
  void generateWriteGPRLH(uint8_t index, Value *val, bxInstruction_c *i, uint8_t low)
  {
     generateWrite8GPR(index, val, low);
  }

  // ========================================
  // Composite functions to manipulate register value.
  // ========================================
  void generateClear64BitHigh(uint8_t index)
  {
    Value *RegVal = generateRead64GPR(index);
    Value *Masked = generateMaskedVal(RegVal, size_mask[3]); 
    generateWrite64GPR(index, Masked);
  }
  Value* generateGetInstructionIv(bxInstruction_c* i)
  {
    LLVMContext &Context = getGlobalContext();
    if (i->os64L()) return ConstantInt::get(Type::getInt64Ty(Context), i->Iq());
    if (i->os32L()) return ConstantInt::get(Type::getInt32Ty(Context), i->Id()); 
    return ConstantInt::get(Type::getInt16Ty(Context), i->Iw());
  }
  Value* generateGetInstructionIb(bxInstruction_c* i)
  {
    LLVMContext &Context = getGlobalContext();
    return ConstantInt::get(Type::getInt8Ty(Context), i->Ib());
  }

  // ========================================
  // Composite functions to manipulate memory value.
  // ========================================
  Value *generateInstruction_FetchXlated(bxInstruction_c* i)
  {
    Type  *osType = getInstruction_osType(i); 
    Value *Xlation = generateInstruction_GetXlated(i);
    Value *LoadPtr = irBuilder->CreateIntToPtr(Xlation, PointerType::getUnqual(osType));
    return irBuilder->CreateLoad(LoadPtr);
  }

  // ========================================
  // Functions to generate IR for flags update. 
  // The FLAGS register is the status register 
  // in Intel x86 microprocessors that contains 
  // the current state of the processor. This 
  // register is 16 bits wide. Its successors, 
  // the EFLAGS and RFLAGS registers, are 32 bits 
  // and 64 bits wide, respectively. The wider 
  // registers retain compatibility with their 
  // smaller predecessors.
  // ========================================
  void generateFlagUpdateIR(void);
 

  // ========================================
  // Functions to generate IR for instructions
  // ========================================
  void generateAsyncCheck(void);
  void generateRunTimeRetIR(void);
  void generate_CallOut_IR(bxInstruction_c* i, uint64_t Func, Value *Opaque);
  void generateInstruction_Interpret_IR(bxInstruction_c* i);
  void generateInstruction_Naked_Interpret_IR(bxInstruction_c* i);
  void generateInstruction_GetResolveModRM_IR(bxInstruction_c* i);
  void generateInstruction_GetModRMXlation_IR(bxInstruction_c* i);
  void generateInstruction_TLBMissCheck_IR(bxInstruction_c* i);
  void generateInstruction_INC_ERX_IR(bxInstruction_c* i);
  void generateInstruction_XCHG_XAX_IR(bxInstruction_c* i);
  void generateInstruction_MOV_ERXIb_IR(bxInstruction_c* i);
  void generateInstruction_MOV_ERXIv_IR(bxInstruction_c* i);
  void generateInstruction_MOV_G8E8R_IR(bxInstruction_c* i);
  void generateInstruction_MOV_GxExR_IR(bxInstruction_c* i);
  void generateInstruction_MOV_GxExM_IR(bxInstruction_c* i);

  Value *generateInstruction_GetXlated(bxInstruction_c *i);
  Value *generateInstruction_GetToXlate(bxInstruction_c *i);

public: 
  /// CreateDefaultIRGenerator - create an emulator ir generator.
  static EmulatorIRGenerator *CreateDefaultIRGenerator(void) 
  {
    EmulatorIRGenerator *NewIR = new EmulatorIRGenerator();
    return NewIR;
  }

  // ========================================
  // drive the generation of IR for instructions
  // ========================================
  void initializeIRGeneration(Function *func)
  {
    current = func;
    irBuilder->SetInsertPoint(&current->back());
  }
  void generateInstructionIR(bxInstruction_c* i);
  void generateEOBIR(Function *current) { generateRunTimeRetIR(); }
};


#endif   // EMULATOR_IR_GENERATOR_H 

